//
//  Steps.m
// 
//

#import "Steps.h"

@interface Steps ()

@property (nonatomic, strong) NSArray *steps;   // void (^)(Steps *steps);
@property (nonatomic, assign) NSUInteger currentStepNumber;
@property (nonatomic, strong) NSMutableDictionary *labels;   // NSString *;
@property (nonatomic, strong) NSString *name;

@property (nonatomic, strong) BeforeStepBlocks beforeBlock;

- (void)start;

@end


@implementation Steps

+ (void)sync: (NSArray *)steps
{
    [self syncWithBeforeBlock:NULL stepName:@"" array:steps];
}

+ (void)syncWithName:(NSString *)name array:(NSArray *)steps
{
    [self syncWithBeforeBlock:NULL stepName:name array:steps];
}

+ (void)syncWithBeforeBlock:(BeforeStepBlocks)block array:(NSArray *)steps
{
    [self syncWithBeforeBlock:block stepName:@"" array:steps];
}

+ (void)syncWithBeforeBlock:(BeforeStepBlocks)block stepName:(NSString *)name array:(NSArray *)steps
{
    Steps *curSteps = [[Steps alloc] init];
    curSteps.name = name;
    curSteps.steps = steps;
    curSteps.beforeBlock = block;
    [curSteps start];
}

- (NSUInteger)stepNumber
{
    return self.currentStepNumber;
}

- (void)start
{
    self.currentStepNumber = 0;
    if ([self.steps count] == 0) {
        return;
    }
    self.beforeBlock(self.currentStepNumber, self.name);
    StepBlocks curStep = self.steps[0];
    curStep(self);
}

- (void)stepWithLabel:(NSString *)label
{
    if (label == nil) {
        return [self step];
    }
    
    if ([self isCurrentLabel:label]) {
        NSLog(@"Ignored, because %p %@ 'step' method is running at (Step number:%lu, Label:%@)", self, self.name, (unsigned long)self.currentStepNumber, label);
        return;
    }
    [self label:label comment:[NSString stringWithFormat:@"step with %@", label]];
    
    self.currentStepNumber++;
    if ([self.steps count] > self.currentStepNumber) {
        if (self.beforeBlock(self.currentStepNumber, self.name) == NO) {
            [self exitWithComment:nil];
            return;
        };
        StepBlocks curStep = self.steps[self.currentStepNumber];
        curStep(self);
    } else {
        NSLog(@"%p %@ 'step' method is too many times called.  (%lu time(s) called, but it must be lower than %lu.)", self, self.name, (unsigned long)self.currentStepNumber, (unsigned long)[self.steps count]);
    }
}

- (void)step
{
    [self stepWithLabel:[NSString stringWithFormat:@"%lu", (unsigned long)self.currentStepNumber]];
}

- (void)label:(NSString *)label comment:(NSString *)comment
{
    NSLog(@"%@ %@ Current step is labeled %@, commented %@", self, self.name, label, comment);
    if (self.labels == nil) {
        self.labels = [[NSMutableDictionary alloc] init];
    }
    [self.labels setObject:@(self.currentStepNumber) forKey:label];
}

- (void)backTo:(NSString *)label
{
    id ret = [self.labels valueForKey:label];
    if (ret == nil || [ret isEqual:[NSNull null]]) {
        NSLog(@"%p %@ Back to undefined label!!! ignore this ops.", self, self.name);
        return;
    }
    if (![ret isKindOfClass:[NSNumber class]]) {
        NSLog(@"%p %@ Labels have non-NSNumber object... What the hell!? ignore this ops.", self, self.name);
        return;
    }
    
    NSNumber *num = (NSNumber *)ret;
    NSInteger newStepNumber = [num integerValue];
    
    if ((NSInteger)[self.steps count] <= newStepNumber || newStepNumber < 0) {
        NSLog(@"%p %@ Label number is out of range. ignore this ops.", self, self.name);
        return;
    }    
    
    self.currentStepNumber = newStepNumber;
    
    NSLog(@"%p %@ Back to %@", self, self.name, label);
    
    self.beforeBlock(self.currentStepNumber, self.name);
    StepBlocks curStep = self.steps[self.currentStepNumber];
    curStep(self);
}

- (BOOL)isCurrentLabel:(NSString *)label
{
    id ret = [self.labels valueForKey:label];
    if (ret == nil || [ret isEqual:[NSNull null]] || ![ret isKindOfClass:[NSNumber class]]) {
        return NO;
    }

    const NSUInteger stepNumber = [(NSNumber *)ret unsignedIntegerValue];
    return stepNumber == self.currentStepNumber;
}

- (void)exitWithComment:(NSString *)comment
{
    NSLog(@"%p %@ Exit steps; commented %@", self.name, self, comment);
    self.steps = nil;
    self.labels = nil;
    self.beforeBlock = NULL;
}


@end
